/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include <stdio.h>
#include "graph.h"
#include "dataControl.h"
#include "controller.h"

#include <string.h>
#include <stdlib.h>
#include <assert.h>

#include <iostream>
#include <fstream>
using namespace std;


typedef struct {
  int from, to;
  vector<string> properties;
} EdgeInfo;


void DataControl::loadColors(const char *file) {
	FILE *fp = fopen(file, "r"); //command execute netzen
	if(fp) {
		char line[256];
		while(!feof(fp)) {
			fgets(line, 256, fp);
			if(!line || strlen(line)==0) continue;
			char preffix[256];
			sscanf(line, "%s\n", preffix);
			if(string(preffix)=="BEGIN") {
				fgets(line, 256, fp);
				char name[128], type[128];
				int read = sscanf(line, "%s %s\n", name, type);
				if(read!=2) {
					error("Incorrect format for color file [%s]. Ignoring rest of the file\n");
					return;
				}
				ColorMap *cmap;
				if(string(type)=="continuous") {
					cmap = new ColorMap(ColorMap::CONTINUOUS);
				} else {
					cmap = new ColorMap(ColorMap::DISCRETE);
				}
				fgets(line, 256, fp);
				float t = 0;
				while(!feof(fp) && string(line)!="END\n") {
					float r, g, b;
					int read1 = sscanf(line,"%f %f %f\n", &r, &g, &b);
					int read2 = sscanf(line,"%f %f %f %f\n",&t, &r, &g, &b);
					if(read2==4) {
						cmap->insert(t, r, g, b);
					} else if(read1==3) {
						cmap->insert(0.0f, r, g, b);
					}
					fgets(line, 256, fp);
				}
				cmap->generate();
				controller->addColormap(name, cmap);
				controller->registerProperty("colormap", name);				
			}
		}
		fclose(fp);
	}
}

void DataControl::loadMetadata(const char *file) {
	ifstream inp(file, ifstream::in);
	if(!inp.is_open()) {
		error("Cannot open file %s\n", file);
		return;
	}    
	string line;
	getline(inp,line);
	while(!inp.eof()) {
		string property = line;
		getline(inp, line);
		int numberElements = atoi(line.c_str());
		for(int i=0;i<numberElements;i++) {
			getline(inp, line);
			string value = line;
			controller->addStringMapping(property, value, i);
		}
		getline(inp, line);
	}	
	inp.close();
}

void DataControl::importEdgeTable(Graph *g, const char *file, bool undirected) {
  assert(g);

  ifstream inp(file, ifstream::in);
  if(!inp.is_open()) {

      strFile2 = "";
      retry = controller->retryOpenFile(file,strFile2);
      if(!retry){
          error("Cannot open file %s\n", file);
          return;
      }
      inp.open(strFile2.c_str(), ifstream::in);

  }    
  string line;
  getline(inp,line);
  if(inp.eof()) {
    error("Metaheader not found\n", file);
    inp.close();
    return;
  }

  char delims[] = "\t\n\r";
  vector<Header> headers;
  char *str = (char*) line.c_str();
  char *tok = strtok(str, delims);
  while(tok) {
    Header h;
    if(string(tok)=="FLOAT") {
      h.type=PROPERTY_FLOAT;
    }
    if(string(tok)=="SIGNED") {
      h.type=PROPERTY_SIGNED;
    }
    if(string(tok)=="STRING") {
      h.type=PROPERTY_STRING;
    }
    if(string(tok)=="INT") {
      h.type=PROPERTY_INT;
    }
    if(string(tok)=="BOOL") {
      h.type=PROPERTY_BOOL;
    }
	  printf("Reading header : %s\n", string(tok).substr(0, string("ORDINAL").length()).c_str());
		if(string(tok).substr(0, string("ORDINAL").length())=="ORDERED") {
			h.type = PROPERTY_ORDINAL;
			h.model = string(tok).substr(string("ORDINAL").length());
		}
    headers.push_back(h);
    tok = strtok(NULL, delims);
  }

  getline(inp,line);
  if(inp.eof()) {
    error("Header not found\n", file);
    inp.close();
    return;
  }

  str = (char*) line.c_str();
  tok = strtok(str, delims);
  int k = 0;
  while(tok) {
    if(k>=(int) headers.size()) {
      error("Header column count does not match meta-header count\n");
      inp.close();
      return;
    }
    print("Reading header %d %s\n", k, tok);
    headers[k++].name = string(tok);
    tok = strtok(NULL, delims);
  }
  if(k<(int) headers.size()) {
    error("Header column count does not match meta-header count\n");
    return;
  }

  vector<EdgeInfo> lines;

  while(!inp.eof()) {
    getline(inp,line);
    char *str = (char*) line.c_str();
    char *tok = strtok(str, delims);
    int k = 0;
    int from = -1, to = -1;

    EdgeInfo line;
    EdgeInfo lineSym;

    while(tok) {
      if(k>=(int) headers.size()) {
	error("Header column count does not match meta-header count\n");
	inp.close();
	return;
      }
      if(headers[k].name=="From") {
	from = atoi(tok);
      }
      if(headers[k].name=="To") {
	to = atoi(tok);
      }

      if(from>=0 && to>=0) {
	line.from = from;
	line.to = to;
	lineSym.from = to;
	lineSym.to = from;
      }
      line.properties.push_back(string(tok));
      lineSym.properties.push_back(string(tok));
      k++;
      tok = strtok(NULL, delims);
    }
    lines.push_back(line);
    if(undirected) lines.push_back(lineSym);
  }
  
  inp.close();

  int maxId = 0;
  set<int> uniqueIds;
  for(unsigned int i=0;i<lines.size();i++) {
    int from = lines[i].from;
    int to = lines[i].to;
    uniqueIds.insert(from);
    uniqueIds.insert(to);
    maxId = max(maxId, from);
    maxId = max(maxId, to);
  }

  if(uniqueIds.size()>=g->nodes.size()) {
    //g->createNodes(g->nodes.size(), uniqueIds.size());
  }

  for(unsigned int i=0;i<lines.size();i++) {
    int from = lines[i].from;
    int to = lines[i].to;
    //print("Adding edge %d %d\n", from, to);
    Edge *edge = g->addEdge(from, to);
    if(!edge) continue;
    
    for(unsigned int k=0;k<headers.size();k++) {
      if(k>=lines[i].properties.size()) break;
			string prop = lines[i].properties[k];
			assert(edge);
			edge->addProperty(headers[k].name, headers[k].type, prop);
			if(headers[k].type == PROPERTY_STRING) {
			  controller->addStringMapping(headers[k].name, prop);
			}
			if(headers[k].type == PROPERTY_ORDINAL) {
				edge->setPropertyOrdinal(headers[k].name, prop, headers[k].model);
      }
    }    
  }

  for(unsigned int i=0;i<headers.size();i++){
      if(headers[i].type==PROPERTY_FLOAT || headers[i].type==PROPERTY_SIGNED) {
          g->computeEdgeMinMaxFloat(headers[i].name);
      }
      else if(headers[i].type==PROPERTY_INT){ 
          g->computeEdgeMinMaxInt(headers[i].name);
      }
      else if(headers[i].type==PROPERTY_BOOL){ 
          g->computeEdgeMinMaxBool(headers[i].name);
      }
      controller->addDataProperty("edge", headers[i].name, headers[i].type);
  }

  bool ifDegreePropExist=  false;
  for(unsigned int i=0;i<g->nodes.size();i++) {
    if(!g->nodes[i]->hasProperty("Degree")) {
      g->nodes[i]->setPropertyFloat("Degree", g->nodes[i]->edges.size());
      ifDegreePropExist = true;
    }
  }
  if(ifDegreePropExist){
      g->computeNodeMinMaxFloat("Degree"); 
      controller->addDataProperty("node", "Degree", PROPERTY_FLOAT);
  }


  print("Finished\n");
}

void DataControl::importTable(Graph *g, const char *file) {
  assert(g);

  ifstream inp(file, ifstream::in);
  if(!inp.is_open()) {

      strFile2 = "";
      retry = controller->retryOpenFile(file, strFile2);
      if(!retry){
          error("Cannot open file %s\n", file);
          return;
      }
      inp.open(strFile2.c_str(), ifstream::in);//);
  }

  string line;
  getline(inp,line);
  if(inp.eof()) {
    error("Metaheader not found\n", file);
    inp.close();
    return;
  }

  char delims[] = "\t\n\r";
  vector<Header> headers;
  char *str = (char*) line.c_str();
  char *tok = strtok(str, delims);
  while(tok) {
    Header h;
    if(string(tok)=="FLOAT") {
      h.type=PROPERTY_FLOAT;
    }
    if(string(tok)=="SIGNED") {
      h.type=PROPERTY_SIGNED;
    }
    if(string(tok)=="STRING") {
      h.type=PROPERTY_STRING;
    }
    if(string(tok)=="INT") {
      h.type=PROPERTY_INT;
    }
	if(string(tok).substr(0, string("ORDINAL").length())=="ORDINAL") {
	  h.type = PROPERTY_ORDINAL;
	  h.model = string(tok).substr(string("ORDINAL").length()+1);
	}

	headers.push_back(h);
    tok = strtok(NULL, delims);
  }

  getline(inp,line);
  if(inp.eof()) {
    error("Header not found\n", file);
    inp.close();
    return;
  }

  str = (char*) line.c_str();
  tok = strtok(str, delims);
  int k = 0;
  while(tok) {
    if(k>=(int) headers.size()) {
      error("Header column count does not match meta-header count\n");
      inp.close();
      return;
    }
    print("Reading header %d %s\n", k, tok);
    headers[k++].name = string(tok);
    tok = strtok(NULL, delims);
  }
  if(k<(int) headers.size()) {
    error("Header column count does not match meta-header count\n");
    return;
  }

  int nodeid=0;
  while(!inp.eof()) {
    getline(inp,line);
    char *str = (char*) line.c_str();
    if(strlen(str)==0) continue;
    //print("Processing line [%s]\n", line.c_str());
    char *tok = strtok(str, delims);
    int k = 0;
    vector<string> properties;
    while(tok) {
      if(k>=(int) headers.size()) {
	error("Header column count does not match meta-header count\n");
	inp.close();
	return;
      }
      if(headers[k].name=="NodeId") {
		  nodeid = atoi(tok);
      }
      properties.push_back(string(tok));
      k++;
      tok = strtok(NULL, delims);
    }

    char strId[20];
    sprintf(strId, "%d", nodeid);
    Node * n = g->addNode(strId);
    n->setPropertyInt("NodeId", nodeid);

    for(unsigned int k=0;k<properties.size();k++) {
      string prop = properties[k];
			assert(n);
			n->addProperty(headers[k].name, headers[k].type, prop);
			if(headers[k].type == PROPERTY_STRING) {
			  controller->addStringMapping(headers[k].name, prop);
			}
			if(headers[k].type == PROPERTY_ORDINAL) {
				n->setPropertyOrdinal(headers[k].name, prop, headers[k].model);
      }
    }
    nodeid++;
  }
  inp.close();


  g->computeNodeMinMaxInt("NodeId");
  for(unsigned int i=0;i<headers.size();i++){
      if(headers[i].type==PROPERTY_FLOAT || headers[i].type==PROPERTY_SIGNED) {
          g->computeNodeMinMaxFloat(headers[i].name);
      }
      else if(headers[i].type==PROPERTY_INT){
          g->computeNodeMinMaxInt(headers[i].name);
      }
      controller->addDataProperty("node", headers[i].name, headers[i].type);
  }

  g->computeNodeMinMaxInt("NodeId");
  controller->addDataProperty("node", "NodeId",PROPERTY_INT);

  print("Import complete [%d nodes]\n", g->nodes.size());
}


void DataControl::importFloat(Graph *g, const char *file, const char *property) {
  assert(g);
  FILE *fp = fopen(file, "r"); 
  if(!fp) {

    error("Cannot open file %s\n", file);
    return;
  }
  for(unsigned int i=0;i<g->nodes.size();i++) {
    float v;
    fscanf(fp, "%f", &v);
    if(!feof(fp)) {
      g->nodes[i]->setPropertyFloat(property, v);
    }
  }
  fclose(fp);
  controller->registerProperty("node",property);
  g->computeNodeMinMaxFloat(property);
}

void DataControl::exportFloat(Graph *g, const char *file, const char *property) {
  assert(g);
  FILE *fp = fopen(file, "w"); 
  if(!fp) {
    error("Cannot open file %s\n", file);
    return;
  }
  for(unsigned int i=0;i<g->nodes.size();i++) {
    float v = g->nodes[i]->getPropertyFloat(property);
    fprintf(fp, "%f ", v);
  }
  fclose(fp);
}


void DataControl::importEdgeFloat(Graph *g, const char *file, const char *property) {
  assert(g);
  FILE *fp = fopen(file, "r"); 
  if(!fp) {
      strFile2 = "";
      retry = controller->retryOpenFile(file,strFile2);
      if(!retry){
          error("Cannot open file %s\n", file);
          return;
      }
      fp = fopen(strFile2.c_str(), "r");
  }
  int n = g->nodes.size();
  while(!feof(fp)) {
    int i, j;
    float v;
    fscanf(fp, "%d %d %f", &i, &j, &v);
    if((i>=0) && (i<n) && (j>=0) && (j<n)) {
      Edge *e = g->nodes[i]->getEdge(j);
      if(e) e->setPropertyFloat(property, v);
    }
  }
  controller->registerProperty("edge",property);
  g->computeEdgeMinMaxFloat(property);
}

void DataControl::exportEdgeFloat(Graph *g, const char *file, const char *property) {
  assert(g);
  FILE *fp = fopen(file, "w"); 
  if(!fp) {
    error("Cannot open file %s\n", file);
    return;
  }
  for(unsigned int i=0;i<g->nodes.size();i++) {
    Node *n0 = g->nodes[i];
    for(unsigned int k=0;k<n0->edges.size();k++) {
      Node  *n1 = n0->edges[k]->getNode1();
      float v = g->nodes[i]->getPropertyFloat(property);
      fprintf(fp, "%d %d %f ", n0->getIndex(), n1->getIndex(), v);
    }
  }
  fclose(fp);
}

void DataControl::importImplicitEdgeFloat(Graph *g, const char *file, const char *property) {
  assert(g);
  FILE *fp = fopen(file, "r"); 
  if(!fp) {
      strFile2 = "";
      retry = controller->retryOpenFile(file,strFile2);
      if(!retry){
          error("Cannot open file %s\n", file);
          return;
      }
      fp = fopen(strFile2.c_str(), "r");
  }
  for(unsigned int i=0;i<g->nodes.size();i++) {
    Node *n0 = g->nodes[i];
    assert(n0);
    n0->createImplicitEdges(g->nodes.size(), g->nodes);
    for(unsigned int j=0;j<g->nodes.size();j++) {
      float v;
      fscanf(fp, "%f", &v);
      if(!feof(fp)) {
	Edge *e = n0->getImplicitEdge(j);
	if(!e) {
	  e = n0->addImplicitEdge(g->nodes[j]);
	}
	assert(e);
	e->setPropertySigned(property, v);
	//if(n0->hasEdge(j)) n0->getEdge(j)->setPropertySigned(property, v);
	if(g->nodes[j]->hasEdge(i)) g->nodes[j]->getEdge(i)->setPropertySigned(property, v);
      }
    }
  }
  fclose(fp);
  controller->registerProperty("edge", property);
  g->computeImplicitEdgeMinMaxFloat(property);
}

void DataControl::exportImplicitEdgeFloat(Graph *g, const char *file, const char *property) {
  assert(g);
  FILE *fp = fopen(file, "w"); 
  if(!fp) {
      strFile2 = "";
      retry = controller->retryOpenFile(file,strFile2);
      if(!retry){
          error("Cannot open file %s\n", file);
          return;
      }
      fp = fopen(strFile2.c_str(), "r");

  }
  for(unsigned int i=0;i<g->nodes.size();i++) {
    Node *n0 = g->nodes[i];
    for(int j=0;j<n0->sizeImplicit;j++) {
      float v = n0->getImplicitEdge(j)->getPropertyFloat(property,0);
      fprintf(fp, "%f ", v);
    }
  }
  fclose(fp);
}

vector<string> DataControl::getFileLines(const char *charFileDirAndName){
    vector <string> vecReturn;

    //string strFileDirAndName = strDirectory + strFileName;
    //const char * charFileDirAndName = strFileDirAndName.c_str();
    //const char * charFileDirAndName = strFileDirAndName.c_str();
    ifstream myfile (charFileDirAndName, ifstream::in);//(thisFileName);

    if( myfile.good() ){
        while(!myfile.eof()){
            string strThisLine;
            getline (myfile,strThisLine); // <iostream>
            if (strThisLine!=""){
                vecReturn.push_back(strThisLine);
            }
        }
    }
    myfile.close();
    return vecReturn;
}

